韓琛準備派遣多個身家較為清白的小弟臥底至香港警隊,包括建明。他向小弟們講述著自己的過去,並說自己不相信算命先生所說的「一將功成萬骨枯」。他認為出來混的,未來的路怎麼走應該由自己決定。
在開始進行所有query前,我們需要先告知EdgeDB初始的schema。
edgedb migration create後,再輸入edgedb migrate。EdgeDB REPL,可以使用更便捷的\migration create及\migrate指令。insert此場景時間1992年insert FuzzyTime {fuzzy_year:= 1992};
insert韓琛及其演員曾志偉韓琛於開頭就說出「一將功成萬骨枯」的經典句,我們將此句收錄在classic_lines property中。
此外,雖然actors為multi link,可以包括多個演員。但是我們可以使用assert_single()來確保最多只會接收到一個曾志偉Actor object。這麼一來,如果資料庫內已經有兩個Actor object的name都叫曾志偉時,這個query就會報錯。
insert Actor {
name:= "曾志偉",
eng_name:= "Eric",
nickname:= "獎老",
};
insert GangsterBoss {
name:= "韓琛",
nickname:= "琛哥",
classic_lines:= ["一將功成萬骨枯"],
actors := assert_single((select Actor filter .name = "曾志偉")),
};
另一種作法是觀察想選擇的object是否有constraint exclusive的property可以作為filter。如果有的話,即代表我們最多只會選擇到一個object,此時就不需要額外使用assert_single()了。這裡由於Actor object沒有constraint exclusive的property,所以無法使用這個作法。
insert劉建明及其少年時期演員陳冠希insert Actor {
name:= "陳冠希",
eng_name:= "Edison",
};
insert GangsterSpy {
name:= "劉建明",
nickname:= "劉仔",
gangster_boss:= assert_single((select GangsterBoss filter .name = "韓琛")),
dept:= "警校學生",
actors := assert_single((select Actor filter .name in {"陳冠希"})),
};
語法與前面類似,留意filter時也可以試試用in {}的寫法。
alias由於每次都要使用(select ... filter ... .xxx=ooo)的語法來選擇object頗為麻煩,針對常用到的object,可以直接在schema中建立alias,方便取用。我們這邊定義了一個hon(韓琛)、lau(劉建明)及year_1992(1992年)的alias:
alias hon:= assert_exists(
assert_single(
(select GangsterBoss filter .name = "韓琛")
)
);
alias lau:= assert_exists(
assert_single(
(select GangsterSpy filter .name = "劉建明")
)
);
alias year_1992:= assert_exists(assert_single((select FuzzyTime
filter .fuzzy_year = 1992
and .fuzzy_month ?= <FuzzyMonth>{}
and .fuzzy_day ?= <FuzzyDay>{}
and .fuzzy_hour ?= <FuzzyHour>{}
and .fuzzy_minute ?= <FuzzyMinute>{}
and .fuzzy_second ?= <FuzzySecond>{}
and .fuzzy_dow ?= <DayOfWeek>{}
))
);
於year_1992中,我們用到了?=operator。?=除了像=可以比較兩個set外,還可以比較空set。當兩個set都為空時,會返回true。當有些property可以為空set時,?=是個非常好用的工具。另外,別忘了空EdgeDBset需要定義型別。
alias year_1992的另一種寫法也可以使用fuzzy_fmt這個computed property來作為filter的條件:
alias year_1992:= assert_exists(
assert_single(
(
select FuzzyTime
filter .fuzzy_fmt="1992/MM/DD_HH24:MI:SS_ID"
)
)
);
這種寫法比較快速,是我實際寫query會用的方法。但在定義schema時,我反而比較喜歡原先那種直接了當的寫法。
alias的function您可能有留意到,我們在alias前都加上了assert_exists()及assert_single(),這樣可以確保每個alias只會返回剛好一個object。我自己會習慣寫一個名為test_alias()的function來做測試:
function test_alias() -> bool
using (all({
test_scene01_alias(),
})
);
function test_scene01_alias() -> bool
using (all({
(exists hon),
(exists lau),
(exists year_1992),
})
);
另外提醒習慣寫Python的朋友,定義function時,在()後不需要加上:。
test_alias()中會包含多個場景的sub-test(如test_scene01_alias()),當每一個場景的sub-test都返回true時,all會返回true,否則會報錯。而我們利用exists檢查各場景中的alias是否存在,如果全部都存在的話,all會返回true,否則會報錯:
edgedb error: CardinalityViolationError: assert_exists violation: expression returned an empty set
這麼一來當我們在操作資料庫時,可以隨時透過test_alias()來確認每一個alias,是否都如預期地返回了剛好一個object。
did you create alias 'default::hon'? [y,n,l,c,b,s,q,?]
> y
did you create alias 'default::lau'? [y,n,l,c,b,s,q,?]
> y
did you create alias 'default::year_1992'? [y,n,l,c,b,s,q,?]
> y
did you create function 'default::test_scene01_alias'? [y,n,l,c,b,s,q,?]
> y
did you create function 'default::test_alias'? [y,n,l,c,b,s,q,?]
> y
test_alias()# end migration needs to be applied before running this query
select test_alias();
如果上述query可以成功執行,就代表我們定義的alias都沒有問題。
insert此場景的Scene因為剛剛已經透過test_alias()測試了所有alias,所以這裡我們可以放心使用。
值得注意的是,我們在這裡使用nested insert,同時insert了Scene object及佛堂這個Location object:
insert Scene {
title:= "韓琛初現",
detail:= "韓琛準備派遣多個身家較為清白的小弟臥底至香港警隊,包括建明。" ++
"他向小弟們講述著自己的過去,並說自己不相信算命先生所說的" ++
"「一將功成萬骨枯」。他認為出來混的,未來的路怎麼走應該由自己決定。",
remarks:= "1.假設此場景為1992年。",
who:= {hon, lau},
`when`:= year_1992,
where:= (insert Location {name:= "佛堂"}) ,
references:= [
(
"維基百科-無間道",
"https://zh.wikipedia.org/zh-tw/%E7%84%A1%E9%96%93%E9%81%93"
),
(
"香港警察職級",
"https://zh.wikipedia.org/zh-tw/%E9%A6%99%E6%B8%AF%E8%AD%A6%E5%AF%9F%E8%81%B7%E7%B4%9A"
)
]
};
理論上,我們應該處理建明由Gangster object轉變到GangsterSpy object的過程,但這對於首幕來說,可能太過複雜,所以在此處直接insert建明為GangsterSpy object。同理,我們將於次幕直接insert永仁為PoliceSpy object,而不處理其由Police object轉變到PoliceSpy object的過程。
佛堂前六個骨灰罈暗指當年韓琛死於屯門的六個兄弟。他藉祭拜為由,於一眾小弟面前展現其「仁義之風」。